<?php
/**
 * @author		João Batista Neto
 * @package		rpo
 * @subpackage	cert
 * @category	OpenSSL, certificates
 * @license		http://opensource.org/licenses/gpl-license.php GNU Public License
 */
namespace rpo\cert;

use \RuntimeException;
use \BadMethodCallException;
use rpo\base\Object;
use rpo\cert\interfaces\IOpenSSLKey;
use rpo\cert\interfaces\IOpenSSLData;
use rpo\cert\interfaces\IOpenSSLCertificateRequest;
use rpo\cert\interfaces\IOpenSSLCertificate;
use rpo\cert\OpenSSLCertificate;

/**
 * Implementação da requisição de certificado
 * @author		João Batista Neto
 * @package		rpo
 * @subpackage	cert
 * @category	OpenSSL, certificates
 * @license		http://opensource.org/licenses/gpl-license.php GNU Public License
 */
class OpenSSLKey extends Object implements IOpenSSLKey {
	/**
	 * @var resource
	 */
	private $resource;

	/**
	 * @var IOpenSSLData
	 */
	private $config;

	/**
	 * Constroi um novo objeto de chave
	 * @param IOpenSSLData $config Dados de configuração
	 * @throws RuntimeException Se não for possível gerar uma nova chave
	 */
	public function __construct( IOpenSSLData $config ){
		if ( ( $this->resource = openssl_pkey_new( $config->getData() ) ) === false ){
			throw new RuntimeException( openssl_error_string() );
		}

		$this->config =& $config;
	}

	/**
	 * Destroi o objeto e libera o recurso da memória
	 */
	public function __destruct(){
		if ( is_resource( $this->resource ) ){
			openssl_pkey_free( $this->resource );
		}
	}

	/**
	 * Retorna uma string representando o objeto exportado
	 * @return string
	 * @throws RuntimeException Se não for possível exportar a chave
	 * @see IOpenSSLExportable::export()
	 */
	public function export(){
		$config = $this->config->getData();
		$pswd = isset( $config[ 'challengePassword' ] ) ? $config[ 'challengePassword' ] : null;
		$ret = null;

		if ( !openssl_pkey_export( $this->resource , $ret , $pswd , $config ) ){
			throw new RuntimeException( openssl_error_string() );
		}

		return $ret;
	}

	/**
	 * Recupera o recurso utilizado pelo objeto
	 * @return resource
	 * @throws BadMethodCallException Se a chave não tiver sido gerada
	 * @see IOpenSSLResource::getResource()
	 */
	public function getResource(){
		$ret = null;

		if ( is_resource( $this->resource ) ){
			$ret = $this->resource;
		} else {
			throw new BadMethodCallException( 'A chave ainda não foi gerada.' );
		}

		return $ret;
	}

	/**
	 * Assina uma requisição de certificado com a chave gerada
	 * @param IOpenSSLCertificateRequest $csr A requisição de certificado
	 * @param IOpenSSLData $config Dados de configuração
	 * @param unknown_type $days Dias para expirar a chave
	 * @param IOpenSSLCertificate $cacert Certificado da CA
	 * @return OpenSSLCertificate O certificado assinado
	 * @throws RuntimeException Se não for possível assinar a requisição de certificado
	 * @uses OpenSSLCertificate
	 */
	public function sign( IOpenSSLCertificateRequest $csr , $days = 365 , IOpenSSLCertificate $cacert = null ){
		$ca = null;

		if ( !is_null( $cacert ) ) $ca = $cacert->getResource();

		$cert = openssl_csr_sign( $csr->getResource() , $ca , $this->resource , (int) $days , $this->config->getData() );

		if ( $cert === false ){
			throw new RuntimeException( openssl_error_string() );
		}

		return new OpenSSLCertificate( $cert );
	}
}